/**
 * Copyright (C) 2010 - 2013 Harry Glasgow
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.jaden.common.config;

import com.googlecode.jaden.common.other.JadenException;
import com.googlecode.jaden.common.other.Utils;
import com.googlecode.jaden.engine.Layer;
import com.googlecode.jaden.ui.Constants;
import com.googlecode.jaden.ui.dialog.automatedtraining.TrainingMode;

import java.io.File;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import java.util.HashMap;

public class ProjectConfiguration implements Serializable {

    /** Version 2.0 */
    private static final long serialVersionUID = 20;

    // Not relevant for persisting.
    private transient boolean modified;
    private transient File currentFile;

    // Details
    private String name = "";
    private String description = "";

    // Definition
    private IOConfiguration[] inputConfigurations = {new IOConfiguration(false), new IOConfiguration(false)};
    private IOConfiguration outputConfiguration = new IOConfiguration(true);

    // TrainingFiles
    private String trainingFilePath = "";
    private boolean validationFromTrainingFile;
    private String validationFilePath = "";
    private int validationDataPercentage = 10;
    private boolean testingFromTrainingFile;
    private String testingFilePath = "";
    private int testingDataPercentage = 10;
    private int firstLine = 1;
    private boolean scanToEnd = true;
    private int lastLine = 1;
    private String columnSeparator = ",";
    private String decimalSeparator = ".";
    private boolean useThousandsSeparator;
    private String thousandsSeparator = ",";
    private boolean useStringLimiter;
    private String stringLimiter = "\"";
    private boolean randomizeData;
    private boolean coalesceDuplicateColumnSeparators;

    // File Mappings
    private int[] inputColumnMappings = {0, 1};
    private int outputColumnMapping = 2;

    // Best config
    private final Map<Integer, Layer> bestLayers = new HashMap<Integer, Layer>();
    private boolean[] bestMask = new boolean[2];

    // Manual training parameters
    private double manualLearningRate = 0.1;
    private double manualMomentum;
    private int manualEpochs = 1000;

    // Automated training parameters.
    private TrainingMode trainingMode = TrainingMode.Forward;
    private int rounds = 1;
    private int jogs = 0;
    private double automatedLearningRate = 0.1;
    private double automatedMomentum = 0;
    private int automatedEpochs = 1000;
    private int minimumLayers = 2;
    private int maximumLayers = 3;

    public ProjectConfiguration() {
        inputConfigurations[0].setName("Input 1");
        inputConfigurations[1].setName("Input 2");
        outputConfiguration.setName("Output");
        bestMask[0] = true;
        bestMask[1] = true;
    }

    public String getValidationError() {
        if (Utils.isBlankOrNull(trainingFilePath)) {
            return Utils.translate(Constants.MESSAGE_TRAINING_FILE_NOT_SET);
        } else if (!new File(trainingFilePath).exists()) {
            return Utils.translate(Constants.MESSAGE_TRAINING_FILE_NOT_VALID);
        }

        if (!validationFromTrainingFile) {
            if (Utils.isBlankOrNull(validationFilePath)) {
                return Utils.translate(Constants.MESSAGE_VALIDATION_FILE_NOT_SET);
            } else if (!new File(validationFilePath).exists()) {
                return Utils.translate(Constants.MESSAGE_VALIDATION_FILE_NOT_VALID);
            }
        } else if (!testingFromTrainingFile) {
            if (Utils.isBlankOrNull(testingFilePath)) {
                return Utils.translate(Constants.MESSAGE_TESTING_FILE_NOT_SET);
            } else if (!new File(testingFilePath).exists()) {
                return Utils.translate(Constants.MESSAGE_TESTING_FILE_NOT_VALID);
            }
        }
        return "";

    }

    public int getInternalInputWidth() {
        int i = 0;
        for (IOConfiguration configuration : inputConfigurations) {
            i += configuration.getInternalWidth();
        }
        return i;
    }

    public int getInternalOutputWidth() {
        return outputConfiguration.getInternalWidth();
    }

    public String getColumnSeparator() {
        return columnSeparator;
    }

    public void setColumnSeparator(String columnSeparator) {
        this.columnSeparator = columnSeparator;
    }

    public File getCurrentFile() {
        return currentFile;
    }

    public void setCurrentFile(File currentFile) {
        this.currentFile = currentFile;
    }

    public String getDecimalSeparator() {
        return decimalSeparator;
    }

    public void setDecimalSeparator(String decimalSeparator) {
        this.decimalSeparator = decimalSeparator;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public int getFirstLine() {
        return firstLine;
    }

    public void setFirstLine(int firstLine) {
        this.firstLine = firstLine;
    }

    public int[] getInputColumnMappings() {
        return inputColumnMappings;
    }

    public void setInputColumnMappings(int[] inputColumnMappings) {
        this.inputColumnMappings = inputColumnMappings;
    }

    public IOConfiguration[] getInputConfigurations() {
        return inputConfigurations;
    }

    public void setInputConfigurations(IOConfiguration[] inputConfigurations) {
        this.inputConfigurations = inputConfigurations;
        bestMask = new boolean[inputConfigurations.length];
        for (int i = 0; i < bestMask.length; i++) {
            bestMask[i] = true;
        }
    }

    public int getLastLine() {
        return lastLine;
    }

    public boolean[] getBestMask() {
        return bestMask;
    }

    public void setBestMask(boolean [] bestMask) throws JadenException {
        if (this.bestMask.length != bestMask.length) {
            // Mask size is set by the size of the input configurations.
            throw new JadenException("Mask length wrong " + this.bestMask.length + ' ' + bestMask.length);
        }
        System.arraycopy(bestMask, 0, this.bestMask, 0, bestMask.length);
    }

    public void setLastLine(int lastLine) {
        this.lastLine = lastLine;
    }

    public int getManualEpochs() {
        return manualEpochs;
    }

    public void setManualEpochs(int manualEpochs) {
        this.manualEpochs = manualEpochs;
    }

    public double getManualLearningRate() {
        return manualLearningRate;
    }

    public void setManualLearningRate(double manualLearningRate) {
        this.manualLearningRate = manualLearningRate;
    }

    public double getManualMomentum() {
        return manualMomentum;
    }

    public void setManualMomentum(double manualMomentum) {
        this.manualMomentum = manualMomentum;
    }

    public Map<Integer, Layer> getBestLayers() {
        return Collections.unmodifiableMap(bestLayers);
    }

    public void putBestLayers(Map<Integer, Layer> bestLayers) {
        this.bestLayers.clear();
        this.bestLayers.putAll(bestLayers);
    }

    public boolean isModified() {
        return modified;
    }

    public void setModified(boolean modified) {
        this.modified = modified;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getOutputColumnMapping() {
        return outputColumnMapping;
    }

    public void setOutputColumnMapping(int outputColumnMapping) {
        this.outputColumnMapping = outputColumnMapping;
    }

    public IOConfiguration getOutputConfiguration() {
        return outputConfiguration;
    }

    public void setOutputConfiguration(IOConfiguration outputConfiguration) {
        this.outputConfiguration = outputConfiguration;
    }

    public boolean isScanToEnd() {
        return scanToEnd;
    }

    public void setScanToEnd(boolean scanToEnd) {
        this.scanToEnd = scanToEnd;
    }

    public String getStringLimiter() {
        return stringLimiter;
    }

    public void setStringLimiter(String stringLimiter) {
        this.stringLimiter = stringLimiter;
    }

    public int getTestingDataPercentage() {
        return testingDataPercentage;
    }

    public void setTestingDataPercentage(int testingDataPercentage) {
        this.testingDataPercentage = testingDataPercentage;
    }

    public String getTestingFilePath() {
        return testingFilePath;
    }

    public void setTestingFilePath(String testingFilePath) {
        this.testingFilePath = testingFilePath;
    }

    public boolean isTestingFromTrainingFile() {
        return testingFromTrainingFile;
    }

    public void setTestingFromTrainingFile(boolean testingFromTrainingFile) {
        this.testingFromTrainingFile = testingFromTrainingFile;
    }

    public String getThousandsSeparator() {
        return thousandsSeparator;
    }

    public void setThousandsSeparator(String thousandsSeparator) {
        this.thousandsSeparator = thousandsSeparator;
    }

    public String getTrainingFilePath() {
        return trainingFilePath;
    }

    public void setTrainingFilePath(String trainingFilePath) {
        this.trainingFilePath = trainingFilePath;
    }

    public boolean isUseStringLimiter() {
        return useStringLimiter;
    }

    public void setUseStringLimiter(boolean useStringLimiter) {
        this.useStringLimiter = useStringLimiter;
    }

    public boolean isUseThousandsSeparator() {
        return useThousandsSeparator;
    }

    public void setUseThousandsSeparator(boolean useThousandsSeparator) {
        this.useThousandsSeparator = useThousandsSeparator;
    }

    public int getValidationDataPercentage() {
        return validationDataPercentage;
    }

    public void setValidationDataPercentage(int validationDataPercentage) {
        this.validationDataPercentage = validationDataPercentage;
    }

    public String getValidationFilePath() {
        return validationFilePath;
    }

    public void setValidationFilePath(String validationFilePath) {
        this.validationFilePath = validationFilePath;
    }

    public boolean isValidationFromTrainingFile() {
        return validationFromTrainingFile;
    }

    public void setValidationFromTrainingFile(boolean validationFromTrainingFile) {
        this.validationFromTrainingFile = validationFromTrainingFile;
    }

    public boolean isRandomizeData() {
        return randomizeData;
    }

    public void setRandomizeData(boolean randomizeData) {
        this.randomizeData = randomizeData;
    }

    /**
     * Get rid of any extraneous data.
     */
    public void compress() {
        for (IOConfiguration configuration : inputConfigurations) {
            configuration.compress();
        }
        outputConfiguration.compress();
    }

    public boolean isCoalesceDuplicateColumnSeparators() {
        return coalesceDuplicateColumnSeparators;
    }

    public void setCoalesceDuplicateColumnSeparators(boolean coalesceDuplicateColumnSeparators) {
        this.coalesceDuplicateColumnSeparators = coalesceDuplicateColumnSeparators;
    }

    public TrainingMode getTrainingMode() {
        return trainingMode;
    }

    public void setTrainingMode(TrainingMode trainingMode) {
        this.trainingMode = trainingMode;
    }

    public int getRounds() {
        return rounds;
    }

    public void setRounds(int rounds) {
        this.rounds = rounds;
    }

    public int getJogs() {
        return jogs;
    }

    public void setJogs(int jogs) {
        this.jogs = jogs;
    }

    public double getAutomatedLearningRate() {
        return automatedLearningRate;
    }

    public void setAutomatedLearningRate(double automatedLearningRate) {
        this.automatedLearningRate = automatedLearningRate;
    }

    public double getAutomatedMomentum() {
        return automatedMomentum;
    }

    public void setAutomatedMomentum(double automatedMomentum) {
        this.automatedMomentum = automatedMomentum;
    }

    public int getAutomatedEpochs() {
        return automatedEpochs;
    }

    public void setAutomatedEpochs(int automatedEpochs) {
        this.automatedEpochs = automatedEpochs;
    }

    public int getMinimumLayers() {
        return minimumLayers;
    }

    public void setMinimumLayers(int minimumLayers) {
        this.minimumLayers = minimumLayers;
    }

    public int getMaximumLayers() {
        return maximumLayers;
    }

    public void setMaximumLayers(int maximumLayers) {
        this.maximumLayers = maximumLayers;
    }
}
